Datasets

Setup dependancies

library(dplyr)
library(tibble)
library("tidyr")
library(ggplot2)
require(scales) 
Loading required package: scales

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(remotes)
#remotes::install_github("Public-Health-Scotland/phsstyles",
##  upgrade = "never"
#)git
library(phsstyles)

Vaccinations

Vaccinations that include the time, for those who have had a serology sample taken

df_vacc <- readRDS("/conf/EAVE/GPanalysis/data/temp/sero_vacc_time.rds") %>% as_tibble()
nrow(df_vacc)
[1] 78473

Serology Primary Care

Load serology Primary Care and print the number of rows of data there are. Also, extract the IgG quantative result as a number.

df_serology_pc <- readRDS("/conf/EAVE/GPanalysis/data/serology_primcare_march22.rds") %>% 
                  as_tibble() %>%
                  dplyr::mutate(IgG = readr::parse_number(test_result_quant))
nrow(df_serology_pc)
[1] 59091

See the column names…

colnames(df_serology_pc)
 [1] "EAVE_LINKNO"       "LabSpecimenNo"     "Sampledate_iso"    "year"              "isoweek"           "sex"              
 [7] "age"               "healthboard"       "SpecimenOrigin"    "DateCollected"     "EcossDateReceived" "SpecimenDate"     
[13] "SpecimenTypeRaw"   "SpecimenType"      "SpecLevel2Group"   "SpecLevel1Group"   "OrganismRaw"       "Type"             
[19] "OriginalOrganism"  "test_result_qual"  "test_result_quant" "QNum"              "Text"              "IgG"              

Make a quick histogram plot of the number of samples collected over time

qplot(df_serology_pc$Sampledate_iso, geom="histogram", fill=I("red"), xlab='Sample Date', ylab='Number of Collected Samples', bins=30) 

Count how many times each person has had a sample taken, and then count how many occurrences there are of people with nsamples

df_serology_pc %>% dplyr::group_by(EAVE_LINKNO) %>% dplyr::summarise(nsamples = dplyr::n()) %>% 
                   dplyr::group_by(nsamples) %>% dplyr::summarise(noccurrences = dplyr::n())

Filter the dataframe to get the number of rows where there has been a positive test result

df_serology_pc_pos <- df_serology_pc %>% dplyr::filter(test_result_qual == "Positive")
nrow(df_serology_pc_pos)
[1] 20439

Serology SNBTS (blood donors)

df_serology_bd <- readRDS("/conf/EAVE/GPanalysis/data/serology_snbts_march22.rds") %>% 
                  as_tibble() %>%
                  dplyr::mutate(IgG = readr::parse_number(test_result_quant))
1 parsing failure.
  row col expected actual
24758  -- a number   SKIP
nrow(df_serology_bd)
[1] 39772
library(phsstyles)

Plot the number of samples collected over time for the blood donors

qplot(df_serology_bd$Sampledate_iso, 
      geom="histogram", 
      xlab='Sample Date', ylab='Number of Collected Samples', bins=40) + scale_colour_discrete_phs(palette = "main")

Again, filter the dataframe to get the number of rows where there has been a positive test result:

df_serology_bd_pos <- df_serology_bd %>% dplyr::filter(test_result_qual == "Positive")
nrow(df_serology_bd_pos)
[1] 18478

Check the number of repeat measurements of a person..

df_serology_bd %>% dplyr::group_by(EAVE_LINKNO) %>% dplyr::summarise(nsamples = dplyr::n()) %>% 
                   dplyr::group_by(nsamples) %>% dplyr::summarise(noccurrences = dplyr::n())

QCOVID (Feb22 Update)

Here we load the QCOVID dataframe and filter it on those EAVE studies that are present in the serology datasets.

df_qcovid <- readRDS("/conf/EAVE/GPanalysis/data/cleaned_data/QCOVID_feb22.rds") %>% 
             as_tibble() 
df_qcovid_pc <- df_qcovid %>% dplyr::filter(EAVE_LINKNO %in% df_serology_pc$EAVE_LINKNO)
nrow(df_qcovid_pc)
[1] 46489
df_qcovid_bd <- df_qcovid %>% dplyr::filter(EAVE_LINKNO %in% df_serology_bd$EAVE_LINKNO)
nrow(df_qcovid_bd)
[1] 23210
df_ana_pc <- df_serology_pc %>% dplyr::left_join(df_qcovid_pc) %>% 
          dplyr::select(c("EAVE_LINKNO","Sampledate_iso","test_result_qual","IgG","n_risk_gps")) %>%
          dplyr::mutate(n_risk_gps = ifelse(is.na(n_risk_gps), "0", levels(n_risk_gps)[n_risk_gps]))
Joining, by = "EAVE_LINKNO"
          
df_ana_pc_pos <- df_ana_pc %>% dplyr::filter(test_result_qual == "Positive")
nrow(df_ana_pc_pos)
[1] 20439
df_ana_pc %>% group_by(n_risk_gps) %>% summarise(counts=n())

Plot a histogram showing the linking of the serology data (primary care) with QCOVID

colnames(df_ana_pc)
[1] "EAVE_LINKNO"      "Sampledate_iso"   "test_result_qual" "IgG"              "n_risk_gps"      
colors <- c("Negative" = phs_colours(c("phs-blue")),"Equivocal" = phs_colours(c("phs-magenta")),"Positive" = phs_colours(c("phs-purple")))
colors
 Negative Equivocal  Positive 
"#0078D4" "#9B4393" "#3F3685" 
n <- nrow(df_ana_pc)
colors <- c("Negative" = phs_colours(c("phs-blue")),"Equivocal" = phs_colours(c("phs-magenta")),"Positive" = phs_colours(c("phs-purple")))
p <- ggplot(df_ana_pc, aes(x=IgG)) +
     geom_histogram(position = "stack", bins=30, data=subset(df_ana_pc,test_result_qual == 'Negative'), aes(fill='Negative')) +
     geom_histogram(position = "stack", bins=30, data=subset(df_ana_pc,test_result_qual == 'Positive'), aes(fill='Positive')) +
     geom_histogram(position = "stack", bins=30, data=subset(df_ana_pc,test_result_qual == 'Equivocal'), aes(fill='Equivocal')) +
     labs(title='Primary Care',x="Antibody Levels [IgG]", y="Number of Samples", fill="Result") +
     scale_fill_manual(values = colors) +
     theme(aspect.ratio = 0.5)  +
     scale_y_log10() +
     scale_x_log10()
p

n <- nrow(df_ana_pc)
p <- ggplot(df_ana_pc, aes(x=IgG, fill=test_result_qual)) +
     geom_histogram(position = "stack", bins=30) +
     labs(title='Primary Care',x="Antibody Levels [IgG]", y="Number of Samples") +
     theme(aspect.ratio = 0.5)  +
     scale_y_log10() +
     scale_x_log10()
p

bw <- 50
n <- nrow(df_ana_pc)
p <- ggplot(df_ana_pc, aes(IgG)) +
     geom_histogram(bins=20) +
     labs(x="Primary Care IgG", y="Number of Samples", fill="Number of Risks (QCOVID)") +
     scale_fill_discrete_phs(palette = "main") + 
     theme(aspect.ratio = 0.5)  
p

df_ana <- df_ana_pc %>% left_join(df_vacc %>% filter(serology_source=='primary_care_serology'))
Joining, by = "EAVE_LINKNO"

Measurements taken after 1st Vac, before 2nd Vac

df_ana_v1 <- df_ana %>% filter(Sampledate_iso > d1_datetime & Sampledate_iso < d2_datetime )
library(lubridate)
# create breaks
breaks <- hour(hm("00:00", "6:00", "12:00", "18:00", "23:59"))
# labels for the breaks
labels <- c("Night", "Morning", "Afternoon", "Evening")
df_ana_v1$Time_of_day <- cut(x=hour(df_ana_v1$d1_datetime), breaks = breaks, labels = labels, include.lowest=TRUE)

Calculate the number of days the measurement was taken after the first vaccine

df_ana_v1 <- df_ana_v1 %>% mutate(days_passed = as.numeric(Sampledate_iso - d1_datetime,units='days'))
ggplot(df_ana_v1, aes(x=days_passed)) + 
  geom_histogram()

ggplot(df_ana_v1, aes(x=IgG)) + 
  geom_histogram()

ggplot(df_ana_v1, aes(x=days_passed, y=mean(IgG))) + 
  geom_boxplot(width=500)

bin_size <- 20
df_ana_v1 %>% 
  mutate(bin_dist = factor(days_passed%/%bin_size)) %>% 
  ggplot(aes(x = bin_dist, y = IgG)) +
  geom_boxplot()

df_ana_v1 %>% select(c("Sampledate_iso","IgG","n_risk_gps","d1_product","d1_datetime","Time_of_day","days_passed")) %>% write.csv('test.csv')

Plot a histogram showing the linking of the serology data with QCOVID

df_ana_bd <- df_serology_bd %>% dplyr::left_join(df_qcovid_bd) %>% 
          dplyr::select(c("EAVE_LINKNO","Sampledate_iso","test_result_qual","IgG","n_risk_gps")) %>%
          dplyr::mutate(n_risk_gps = ifelse(is.na(n_risk_gps), "0", levels(n_risk_gps)[n_risk_gps]))
Joining, by = "EAVE_LINKNO"
          
df_ana_bd_pos <- df_ana_bd %>% dplyr::filter(test_result_qual == "Positive")
min(df_ana_bd_pos$IgG) 
[1] 1.1
max(df_ana_bd_pos$IgG) 
[1] 11

Number of risk groups in the dataset

df_ana_bd %>% group_by(n_risk_gps) %>% summarise(n=n())
n <- nrow(df_ana_bd)
colors <- c("Negative" = phs_colours(c("phs-blue")),"Equivocal" = phs_colours(c("phs-magenta")),"Positive" = phs_colours(c("phs-purple")))
p <- ggplot(df_ana_bd, aes(x=IgG)) +
     geom_histogram(position = "stack", bins=40, data=subset(df_ana_bd,test_result_qual == 'Negative'), aes(fill='Negative')) +
     geom_histogram(position = "stack", bins=40, data=subset(df_ana_bd,test_result_qual == 'Positive'), aes(fill='Positive')) +
     geom_histogram(position = "stack", bins=40, data=subset(df_ana_bd,test_result_qual == 'Equivocal'), aes(fill='Equivocal')) +
     labs(title='Blood Donors',x="Antibody Levels [IgG]", y="Number of Samples", fill="Result") +
     scale_fill_manual(values = colors) +
     theme(aspect.ratio = 0.5)  +
     scale_y_log10() 
p

All data

bw <- 0.5
n <- nrow(df_ana_bd_pos)
p <- ggplot(df_ana_bd_pos, aes(x=IgG, color=n_risk_gps)) +
     stat_bin(geom="step",bins=10,position='identity', size=2, alpha=0.8) +
     xlim(0,10) +
     labs(title='Blood Donors',x="IgG", y="Number of Samples", color="Number of Risks (QCOVID)") +
     scale_colour_discrete_phs(palette = "all") + 
     scale_y_continuous(trans = "log10") +
     theme(aspect.ratio = 0.5) 
p

Positive samples only…

df_ana_bd_pos %>% group_by(n_risk_gps) %>% summarise(n=n())
bw <- 100
n <- nrow(df_ana_pc_pos)
p <- ggplot(df_ana_pc_pos, aes(x=IgG, color=n_risk_gps)) +
     stat_bin(geom="step",bins=10,position='identity', size=2, alpha=0.8) +
     xlim(0,2000) +
     labs(title='Primary Care',x="IgG", y="Number of Samples", color="Number of Risks (QCOVID)") +
     scale_colour_discrete_phs(palette = "all") + 
     scale_y_continuous(trans = "log10") +
     theme(aspect.ratio = 0.5) 
p

bw <- 0.5
n <- nrow(df_ana_bd_pos)
p <- ggplot(df_ana_bd_pos, aes(x=IgG, fill=n_risk_gps)) +
     geom_histogram(binwidth = bw) + xlim(0,11+bw) +
     labs(x="Blood Donors IgG", y="Number of Samples", fill="Number of Risks (QCOVID)") +
     scale_fill_discrete_phs(palette = "main") + 
     scale_y_log10() +
     theme(aspect.ratio = 0.5) 
p

df_ana_bd
df <- readRDS("/conf/EAVE/GPanalysis/data/serology_primcare_march22.rds") %>% dplyr::left_join(df_qcovid_pc) %>% mutate(age_group = cut(age,right=FALSE, breaks = c(0,10,20,30,40,50,60,70,80,90,1000),
                               label=c("0-9", 
                                       "10-19", 
                                       "20-29",
                                       "30-39", 
                                       "40-49",
                                       "50-59",
                                       "60-69",
                                       "70-79",
                                       "80-89",
                                       "90+"))) 
Joining, by = "EAVE_LINKNO"
df
max_prop <- 30     # choose the highest proportion you want to show 
step <- 5           # choose the space you want beween labels 
## this part defines vector using the above numbers with axis breaks
breaks <- c(
    seq(max_prop/100 * -1, 0 - step/100, step/100), 
    0, 
    seq(0 + step / 100, max_prop/100, step/100)
    )
## this part defines vector using the above numbers with axis limits
limits <- c(max_prop/100 * -1, max_prop/100)
## this part defines vector using the above numbers with axis labels
labels <-  c(
      seq(max_prop, step, -step), 
      0, 
      seq(step, max_prop, step)
    )
p1 <- df %>% 
  ## make sure the variables are factors
  mutate(age_group = factor(n_risk_gps), 
         sex = factor(sex)) %>%
  age_pyramid(
    age_group = "age_group",
    split_by = "sex", 
    proportion = TRUE) +
  ## only show the x axis label (otherwise repeated in all three plots)
  labs(title = "Primary Care", 
       x = "N Risk Groups", 
       y = "% of Sample")  + 
  ## make the x axis the same for all plots 
  scale_y_continuous(breaks = breaks, 
    limits = limits, 
    labels = labels)
9454 missing rows were removed (9454 values from `age_group` and 0 values from `sex`).Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
p1

df <- readRDS("/conf/EAVE/GPanalysis/data/serology_snbts_march22.rds") %>% dplyr::left_join(df_qcovid_bd)
Joining, by = "EAVE_LINKNO"
df
max_prop <- 40     # choose the highest proportion you want to show 
step <- 5           # choose the space you want beween labels 
## this part defines vector using the above numbers with axis breaks
breaks <- c(
    seq(max_prop/100 * -1, 0 - step/100, step/100), 
    0, 
    seq(0 + step / 100, max_prop/100, step/100)
    )
## this part defines vector using the above numbers with axis limits
limits <- c(max_prop/100 * -1, max_prop/100)
## this part defines vector using the above numbers with axis labels
labels <-  c(
      seq(max_prop, step, -step), 
      0, 
      seq(step, max_prop, step)
    )
p2 <- df %>% 
  ## make sure the variables are factors
  mutate(age_group = factor(n_risk_gps), 
         sex = factor(sex)) %>%
  age_pyramid(
    age_group = "age_group",
    split_by = "sex", 
    proportion = TRUE) +
  ## only show the x axis label (otherwise repeated in all three plots)
  labs(title = "Blood Donors", 
       x = "N Risk Groups", 
       y = "% of Sample")  + 
  ## make the x axis the same for all plots 
  scale_y_continuous(breaks = breaks, 
    limits = limits, 
    labels = labels)
10775 missing rows were removed (10775 values from `age_group` and 0 values from `sex`).Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
p2

df <- readRDS("/conf/EAVE/GPanalysis/data/EAVE_demographics_SK.rds") %>% left_join(df_qcovid)
Joining, by = c("EAVE_LINKNO", "Sex")
df
max_prop <- 30     # choose the highest proportion you want to show 
step <- 5           # choose the space you want beween labels 
## this part defines vector using the above numbers with axis breaks
breaks <- c(
    seq(max_prop/100 * -1, 0 - step/100, step/100), 
    0, 
    seq(0 + step / 100, max_prop/100, step/100)
    )
## this part defines vector using the above numbers with axis limits
limits <- c(max_prop/100 * -1, max_prop/100)
## this part defines vector using the above numbers with axis labels
labels <-  c(
      seq(max_prop, step, -step), 
      0, 
      seq(step, max_prop, step)
    )
p3 <- df %>% 
  ## make sure the variables are factors
  mutate(age_group = factor(n_risk_gps), 
         sex = factor(Sex)) %>%
  age_pyramid(
    age_group = "age_group",
    split_by = "sex", 
    proportion = TRUE) +
  ## only show the x axis label (otherwise repeated in all three plots)
  labs(title = "EAVE-II", 
       x = "N Risk Groups", 
       y = "% of Sample")  + 
  ## make the x axis the same for all plots 
  scale_y_continuous(breaks = breaks, 
    limits = limits, 
    labels = labels)
1760162 missing rows were removed (1760162 values from `age_group` and 0 values from `sex`).Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
p3

grid.arrange(p3,p1,p2,ncol=3,nrow=1)


max_prop <- 40     # choose the highest proportion you want to show 
step <- 5           # choose the space you want beween labels 

## this part defines vector using the above numbers with axis breaks
breaks <- c(
    seq(max_prop/100 * -1, 0 - step/100, step/100), 
    0, 
    seq(0 + step / 100, max_prop/100, step/100)
    )

## this part defines vector using the above numbers with axis limits
limits <- c(max_prop/100 * -1, max_prop/100)

## this part defines vector using the above numbers with axis labels
labels <-  c(
      seq(max_prop, step, -step), 
      0, 
      seq(step, max_prop, step)
    )


 

p2 <- df %>% 
  ## make sure the variables are factors
  mutate(age_group = factor(age_group), 
         sex = factor(Sex)) %>%
  age_pyramid(
    age_group = "age_group",
    split_by = "sex", 
    proportion = TRUE) +
  ## only show the x axis label (otherwise repeated in all three plots)
  labs(title = "Blood Donors", 
       x = "N Risk Groups", 
       y = "% of Sample")  + 
  ## make the x axis the same for all plots 
  scale_y_continuous(breaks = breaks, 
    limits = limits, 
    labels = labels)

grid.arrange(p1,p2,p2,ncol=3,nrow=1)
LS0tCnRpdGxlOiAiU2Vyb2xvZ3kgRGF0YSBFeHBsb3JhdGlvbiBNYXJjaCAyMDIyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMjIERhdGFzZXRzCgoKU2V0dXAgZGVwZW5kYW5jaWVzCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeSgidGlkeXIiKQpsaWJyYXJ5KGdncGxvdDIpCnJlcXVpcmUoc2NhbGVzKSAKbGlicmFyeShyZW1vdGVzKQojcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoIlB1YmxpYy1IZWFsdGgtU2NvdGxhbmQvcGhzc3R5bGVzIiwKIyMgIHVwZ3JhZGUgPSAibmV2ZXIiCiMpZ2l0CmBgYAoKYGBge3J9CmxpYnJhcnkocGhzc3R5bGVzKQpgYGAKCgoKIyMjIFZhY2NpbmF0aW9ucwoKVmFjY2luYXRpb25zIHRoYXQgaW5jbHVkZSB0aGUgdGltZSwgZm9yIHRob3NlIHdobyBoYXZlIGhhZCBhIHNlcm9sb2d5IHNhbXBsZSB0YWtlbgoKYGBge3J9CmRmX3ZhY2MgPC0gcmVhZFJEUygiL2NvbmYvRUFWRS9HUGFuYWx5c2lzL2RhdGEvdGVtcC9zZXJvX3ZhY2NfdGltZS5yZHMiKSAlPiUgYXNfdGliYmxlKCkKbnJvdyhkZl92YWNjKQpgYGAKCgojIyMgU2Vyb2xvZ3kgUHJpbWFyeSBDYXJlIAoKTG9hZCBzZXJvbG9neSBQcmltYXJ5IENhcmUgYW5kIHByaW50IHRoZSBudW1iZXIgb2Ygcm93cyBvZiBkYXRhIHRoZXJlIGFyZS4gQWxzbywgZXh0cmFjdCB0aGUgSWdHIHF1YW50YXRpdmUgcmVzdWx0IGFzIGEgbnVtYmVyLgpgYGB7cn0KCmRmX3Nlcm9sb2d5X3BjIDwtIHJlYWRSRFMoIi9jb25mL0VBVkUvR1BhbmFseXNpcy9kYXRhL3Nlcm9sb2d5X3ByaW1jYXJlX21hcmNoMjIucmRzIikgJT4lIAogICAgICAgICAgICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgZHBseXI6Om11dGF0ZShJZ0cgPSByZWFkcjo6cGFyc2VfbnVtYmVyKHRlc3RfcmVzdWx0X3F1YW50KSkKCm5yb3coZGZfc2Vyb2xvZ3lfcGMpCmBgYAoKU2VlIHRoZSBjb2x1bW4gbmFtZXMuLi4KYGBge3J9CmNvbG5hbWVzKGRmX3Nlcm9sb2d5X3BjKQpgYGAKCmBgYAoKYGBgCgpNYWtlIGEgcXVpY2sgaGlzdG9ncmFtIHBsb3Qgb2YgdGhlIG51bWJlciBvZiBzYW1wbGVzIGNvbGxlY3RlZCBvdmVyIHRpbWUKYGBge3J9CnFwbG90KGRmX3Nlcm9sb2d5X3BjJFNhbXBsZWRhdGVfaXNvLCBnZW9tPSJoaXN0b2dyYW0iLCBmaWxsPUkoInJlZCIpLCB4bGFiPSdTYW1wbGUgRGF0ZScsIHlsYWI9J051bWJlciBvZiBDb2xsZWN0ZWQgU2FtcGxlcycsIGJpbnM9MzApIApgYGAKCgoKQ291bnQgaG93IG1hbnkgdGltZXMgZWFjaCBwZXJzb24gaGFzIGhhZCBhIHNhbXBsZSB0YWtlbiwgYW5kIHRoZW4gY291bnQgaG93IG1hbnkgb2NjdXJyZW5jZXMgdGhlcmUgYXJlIG9mIHBlb3BsZSB3aXRoIGBuc2FtcGxlc2AKYGBge3J9CmRmX3Nlcm9sb2d5X3BjICU+JSBkcGx5cjo6Z3JvdXBfYnkoRUFWRV9MSU5LTk8pICU+JSBkcGx5cjo6c3VtbWFyaXNlKG5zYW1wbGVzID0gZHBseXI6Om4oKSkgJT4lIAogICAgICAgICAgICAgICAgICAgZHBseXI6Omdyb3VwX2J5KG5zYW1wbGVzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShub2NjdXJyZW5jZXMgPSBkcGx5cjo6bigpKQpgYGAKCgoKRmlsdGVyIHRoZSBkYXRhZnJhbWUgdG8gZ2V0IHRoZSBudW1iZXIgb2Ygcm93cyB3aGVyZSB0aGVyZSBoYXMgYmVlbiBhIHBvc2l0aXZlIHRlc3QgcmVzdWx0CmBgYHtyfQoKZGZfc2Vyb2xvZ3lfcGNfcG9zIDwtIGRmX3Nlcm9sb2d5X3BjICU+JSBkcGx5cjo6ZmlsdGVyKHRlc3RfcmVzdWx0X3F1YWwgPT0gIlBvc2l0aXZlIikKbnJvdyhkZl9zZXJvbG9neV9wY19wb3MpCmBgYAoKCgojIyMgU2Vyb2xvZ3kgU05CVFMgKGJsb29kIGRvbm9ycykKCgpgYGB7cn0KZGZfc2Vyb2xvZ3lfYmQgPC0gcmVhZFJEUygiL2NvbmYvRUFWRS9HUGFuYWx5c2lzL2RhdGEvc2Vyb2xvZ3lfc25idHNfbWFyY2gyMi5yZHMiKSAlPiUgCiAgICAgICAgICAgICAgICAgIGFzX3RpYmJsZSgpICU+JQogICAgICAgICAgICAgICAgICBkcGx5cjo6bXV0YXRlKElnRyA9IHJlYWRyOjpwYXJzZV9udW1iZXIodGVzdF9yZXN1bHRfcXVhbnQpKQoKbnJvdyhkZl9zZXJvbG9neV9iZCkKCmBgYAoKYGBge3J9CmxpYnJhcnkocGhzc3R5bGVzKQpgYGAKCgoKUGxvdCB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgY29sbGVjdGVkIG92ZXIgdGltZSBmb3IgdGhlIGJsb29kIGRvbm9ycwpgYGB7cn0KcXBsb3QoZGZfc2Vyb2xvZ3lfYmQkU2FtcGxlZGF0ZV9pc28sIAogICAgICBnZW9tPSJoaXN0b2dyYW0iLCAKICAgICAgeGxhYj0nU2FtcGxlIERhdGUnLCB5bGFiPSdOdW1iZXIgb2YgQ29sbGVjdGVkIFNhbXBsZXMnLCBiaW5zPTQwKSArIHNjYWxlX2NvbG91cl9kaXNjcmV0ZV9waHMocGFsZXR0ZSA9ICJtYWluIikKYGBgCgoKQWdhaW4sIGZpbHRlciB0aGUgZGF0YWZyYW1lIHRvIGdldCB0aGUgbnVtYmVyIG9mIHJvd3Mgd2hlcmUgdGhlcmUgaGFzIGJlZW4gYSBwb3NpdGl2ZSB0ZXN0IHJlc3VsdDoKYGBge3J9CgpkZl9zZXJvbG9neV9iZF9wb3MgPC0gZGZfc2Vyb2xvZ3lfYmQgJT4lIGRwbHlyOjpmaWx0ZXIodGVzdF9yZXN1bHRfcXVhbCA9PSAiUG9zaXRpdmUiKQpucm93KGRmX3Nlcm9sb2d5X2JkX3BvcykKYGBgCgpDaGVjayB0aGUgbnVtYmVyIG9mIHJlcGVhdCBtZWFzdXJlbWVudHMgb2YgYSBwZXJzb24uLiAKYGBge3J9CmRmX3Nlcm9sb2d5X2JkICU+JSBkcGx5cjo6Z3JvdXBfYnkoRUFWRV9MSU5LTk8pICU+JSBkcGx5cjo6c3VtbWFyaXNlKG5zYW1wbGVzID0gZHBseXI6Om4oKSkgJT4lIAogICAgICAgICAgICAgICAgICAgZHBseXI6Omdyb3VwX2J5KG5zYW1wbGVzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShub2NjdXJyZW5jZXMgPSBkcGx5cjo6bigpKQpgYGAKCgojIyMgUUNPVklEIChGZWIyMiBVcGRhdGUpCgpIZXJlIHdlIGxvYWQgdGhlIFFDT1ZJRCBkYXRhZnJhbWUgYW5kIGZpbHRlciBpdCBvbiB0aG9zZSBFQVZFIHN0dWRpZXMgdGhhdCBhcmUgcHJlc2VudCBpbiB0aGUgc2Vyb2xvZ3kgZGF0YXNldHMuCgpgYGB7cn0KZGZfcWNvdmlkIDwtIHJlYWRSRFMoIi9jb25mL0VBVkUvR1BhbmFseXNpcy9kYXRhL2NsZWFuZWRfZGF0YS9RQ09WSURfZmViMjIucmRzIikgJT4lIAogICAgICAgICAgICAgYXNfdGliYmxlKCkgCgpkZl9xY292aWRfcGMgPC0gZGZfcWNvdmlkICU+JSBkcGx5cjo6ZmlsdGVyKEVBVkVfTElOS05PICVpbiUgZGZfc2Vyb2xvZ3lfcGMkRUFWRV9MSU5LTk8pCm5yb3coZGZfcWNvdmlkX3BjKQoKZGZfcWNvdmlkX2JkIDwtIGRmX3Fjb3ZpZCAlPiUgZHBseXI6OmZpbHRlcihFQVZFX0xJTktOTyAlaW4lIGRmX3Nlcm9sb2d5X2JkJEVBVkVfTElOS05PKQpucm93KGRmX3Fjb3ZpZF9iZCkKCmBgYAoKCmBgYHtyfQpkZl9hbmFfcGMgPC0gZGZfc2Vyb2xvZ3lfcGMgJT4lIGRwbHlyOjpsZWZ0X2pvaW4oZGZfcWNvdmlkX3BjKSAlPiUgCiAgICAgICAgICBkcGx5cjo6c2VsZWN0KGMoIkVBVkVfTElOS05PIiwiU2FtcGxlZGF0ZV9pc28iLCJ0ZXN0X3Jlc3VsdF9xdWFsIiwiSWdHIiwibl9yaXNrX2dwcyIpKSAlPiUKICAgICAgICAgIGRwbHlyOjptdXRhdGUobl9yaXNrX2dwcyA9IGlmZWxzZShpcy5uYShuX3Jpc2tfZ3BzKSwgIjAiLCBsZXZlbHMobl9yaXNrX2dwcylbbl9yaXNrX2dwc10pKQogICAgICAgICAgCgpkZl9hbmFfcGNfcG9zIDwtIGRmX2FuYV9wYyAlPiUgZHBseXI6OmZpbHRlcih0ZXN0X3Jlc3VsdF9xdWFsID09ICJQb3NpdGl2ZSIpCm5yb3coZGZfYW5hX3BjX3BvcykKYGBgCgpgYGB7cn0KZGZfYW5hX3BjICU+JSBncm91cF9ieShuX3Jpc2tfZ3BzKSAlPiUgc3VtbWFyaXNlKGNvdW50cz1uKCkpCmBgYAoKClBsb3QgYSBoaXN0b2dyYW0gc2hvd2luZyB0aGUgbGlua2luZyBvZiB0aGUgc2Vyb2xvZ3kgZGF0YSAocHJpbWFyeSBjYXJlKSB3aXRoIFFDT1ZJRAoKCmBgYHtyfQpjb2xuYW1lcyhkZl9hbmFfcGMpCmBgYAoKCmBgYHtyfQpjb2xvcnMgPC0gYygiTmVnYXRpdmUiID0gcGhzX2NvbG91cnMoYygicGhzLWJsdWUiKSksIkVxdWl2b2NhbCIgPSBwaHNfY29sb3VycyhjKCJwaHMtbWFnZW50YSIpKSwiUG9zaXRpdmUiID0gcGhzX2NvbG91cnMoYygicGhzLXB1cnBsZSIpKSkKY29sb3JzCmBgYAoKYGBge3J9CgpuIDwtIG5yb3coZGZfYW5hX3BjKQpjb2xvcnMgPC0gYygiTmVnYXRpdmUiID0gcGhzX2NvbG91cnMoYygicGhzLWJsdWUiKSksIkVxdWl2b2NhbCIgPSBwaHNfY29sb3VycyhjKCJwaHMtbWFnZW50YSIpKSwiUG9zaXRpdmUiID0gcGhzX2NvbG91cnMoYygicGhzLXB1cnBsZSIpKSkKCgpwIDwtIGdncGxvdChkZl9hbmFfcGMsIGFlcyh4PUlnRykpICsKICAgICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJzdGFjayIsIGJpbnM9MzAsIGRhdGE9c3Vic2V0KGRmX2FuYV9wYyx0ZXN0X3Jlc3VsdF9xdWFsID09ICdOZWdhdGl2ZScpLCBhZXMoZmlsbD0nTmVnYXRpdmUnKSkgKwogICAgIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gInN0YWNrIiwgYmlucz0zMCwgZGF0YT1zdWJzZXQoZGZfYW5hX3BjLHRlc3RfcmVzdWx0X3F1YWwgPT0gJ1Bvc2l0aXZlJyksIGFlcyhmaWxsPSdQb3NpdGl2ZScpKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAic3RhY2siLCBiaW5zPTMwLCBkYXRhPXN1YnNldChkZl9hbmFfcGMsdGVzdF9yZXN1bHRfcXVhbCA9PSAnRXF1aXZvY2FsJyksIGFlcyhmaWxsPSdFcXVpdm9jYWwnKSkgKwogICAgIGxhYnModGl0bGU9J1ByaW1hcnkgQ2FyZScseD0iQW50aWJvZHkgTGV2ZWxzIFtJZ0ddIiwgeT0iTnVtYmVyIG9mIFNhbXBsZXMiLCBmaWxsPSJSZXN1bHQiKSArCiAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMC41KSAgKwogICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICAgc2NhbGVfeF9sb2cxMCgpCgpwCmBgYAoKCmBgYHtyfQoKbiA8LSBucm93KGRmX2FuYV9wYykKcCA8LSBnZ3Bsb3QoZGZfYW5hX3BjLCBhZXMoeD1JZ0csIGZpbGw9dGVzdF9yZXN1bHRfcXVhbCkpICsKICAgICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJzdGFjayIsIGJpbnM9MzApICsKICAgICBsYWJzKHRpdGxlPSdQcmltYXJ5IENhcmUnLHg9IkFudGlib2R5IExldmVscyBbSWdHXSIsIHk9Ik51bWJlciBvZiBTYW1wbGVzIikgKwogICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNSkgICsKICAgICBzY2FsZV95X2xvZzEwKCkgKwogICAgIHNjYWxlX3hfbG9nMTAoKQpwCmBgYAoKCgpgYGB7cn0KYncgPC0gNTAKbiA8LSBucm93KGRmX2FuYV9wYykKcCA8LSBnZ3Bsb3QoZGZfYW5hX3BjLCBhZXMoSWdHKSkgKwogICAgIGdlb21faGlzdG9ncmFtKGJpbnM9MjApICsKICAgICBsYWJzKHg9IlByaW1hcnkgQ2FyZSBJZ0ciLCB5PSJOdW1iZXIgb2YgU2FtcGxlcyIsIGZpbGw9Ik51bWJlciBvZiBSaXNrcyAoUUNPVklEKSIpICsKICAgICBzY2FsZV9maWxsX2Rpc2NyZXRlX3BocyhwYWxldHRlID0gIm1haW4iKSArIAogICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNSkgIApwCmBgYAoKCmBgYHtyfQpkZl9hbmEgPC0gZGZfYW5hX3BjICU+JSBsZWZ0X2pvaW4oZGZfdmFjYyAlPiUgZmlsdGVyKHNlcm9sb2d5X3NvdXJjZT09J3ByaW1hcnlfY2FyZV9zZXJvbG9neScpKQpgYGAKCgpNZWFzdXJlbWVudHMgdGFrZW4gYWZ0ZXIgMXN0IFZhYywgYmVmb3JlIDJuZCBWYWMKYGBge3J9CmRmX2FuYV92MSA8LSBkZl9hbmEgJT4lIGZpbHRlcihTYW1wbGVkYXRlX2lzbyA+IGQxX2RhdGV0aW1lICYgU2FtcGxlZGF0ZV9pc28gPCBkMl9kYXRldGltZSApCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgpgYGB7cn0KCiMgY3JlYXRlIGJyZWFrcwpicmVha3MgPC0gaG91cihobSgiMDA6MDAiLCAiNjowMCIsICIxMjowMCIsICIxODowMCIsICIyMzo1OSIpKQojIGxhYmVscyBmb3IgdGhlIGJyZWFrcwpsYWJlbHMgPC0gYygiTmlnaHQiLCAiTW9ybmluZyIsICJBZnRlcm5vb24iLCAiRXZlbmluZyIpCgpkZl9hbmFfdjEkVGltZV9vZl9kYXkgPC0gY3V0KHg9aG91cihkZl9hbmFfdjEkZDFfZGF0ZXRpbWUpLCBicmVha3MgPSBicmVha3MsIGxhYmVscyA9IGxhYmVscywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkKYGBgCgpDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBkYXlzIHRoZSBtZWFzdXJlbWVudCB3YXMgdGFrZW4gYWZ0ZXIgdGhlIGZpcnN0IHZhY2NpbmUgCgpgYGB7cn0KZGZfYW5hX3YxIDwtIGRmX2FuYV92MSAlPiUgbXV0YXRlKGRheXNfcGFzc2VkID0gYXMubnVtZXJpYyhTYW1wbGVkYXRlX2lzbyAtIGQxX2RhdGV0aW1lLHVuaXRzPSdkYXlzJykpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGZfYW5hX3YxLCBhZXMoeD1kYXlzX3Bhc3NlZCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGZfYW5hX3YxLCBhZXMoeD1JZ0cpKSArIAogIGdlb21faGlzdG9ncmFtKCkKYGBgCgoKCmBgYHtyfQpnZ3Bsb3QoZGZfYW5hX3YxLCBhZXMoeD1kYXlzX3Bhc3NlZCwgeT1tZWFuKElnRykpKSArIAogIGdlb21fYm94cGxvdCh3aWR0aD01MDApCgpgYGAKCgoKYGBge3J9CmJpbl9zaXplIDwtIDIwCgpkZl9hbmFfdjEgJT4lIAogIG11dGF0ZShiaW5fZGlzdCA9IGZhY3RvcihkYXlzX3Bhc3NlZCUvJWJpbl9zaXplKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGJpbl9kaXN0LCB5ID0gSWdHKSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CmRmX2FuYV92MSAlPiUgc2VsZWN0KGMoIlNhbXBsZWRhdGVfaXNvIiwiSWdHIiwibl9yaXNrX2dwcyIsImQxX3Byb2R1Y3QiLCJkMV9kYXRldGltZSIsIlRpbWVfb2ZfZGF5IiwiZGF5c19wYXNzZWQiKSkgJT4lIHdyaXRlLmNzdigndGVzdC5jc3YnKQpgYGAKCgoKUGxvdCBhIGhpc3RvZ3JhbSBzaG93aW5nIHRoZSBsaW5raW5nIG9mIHRoZSBzZXJvbG9neSBkYXRhIHdpdGggUUNPVklECmBgYHtyfQpkZl9hbmFfYmQgPC0gZGZfc2Vyb2xvZ3lfYmQgJT4lIGRwbHlyOjpsZWZ0X2pvaW4oZGZfcWNvdmlkX2JkKSAlPiUgCiAgICAgICAgICBkcGx5cjo6c2VsZWN0KGMoIkVBVkVfTElOS05PIiwiU2FtcGxlZGF0ZV9pc28iLCJ0ZXN0X3Jlc3VsdF9xdWFsIiwiSWdHIiwibl9yaXNrX2dwcyIpKSAlPiUKICAgICAgICAgIGRwbHlyOjptdXRhdGUobl9yaXNrX2dwcyA9IGlmZWxzZShpcy5uYShuX3Jpc2tfZ3BzKSwgIjAiLCBsZXZlbHMobl9yaXNrX2dwcylbbl9yaXNrX2dwc10pKQogICAgICAgICAgCgpkZl9hbmFfYmRfcG9zIDwtIGRmX2FuYV9iZCAlPiUgZHBseXI6OmZpbHRlcih0ZXN0X3Jlc3VsdF9xdWFsID09ICJQb3NpdGl2ZSIpCmBgYApgYGB7cn0KbWluKGRmX2FuYV9iZF9wb3MkSWdHKSAKbWF4KGRmX2FuYV9iZF9wb3MkSWdHKSAKYGBgCgoKTnVtYmVyIG9mIHJpc2sgZ3JvdXBzIGluIHRoZSBkYXRhc2V0CgpgYGB7cn0KZGZfYW5hX2JkICU+JSBncm91cF9ieShuX3Jpc2tfZ3BzKSAlPiUgc3VtbWFyaXNlKG49bigpKQpgYGAKCgpgYGB7cn0KCm4gPC0gbnJvdyhkZl9hbmFfYmQpCmNvbG9ycyA8LSBjKCJOZWdhdGl2ZSIgPSBwaHNfY29sb3VycyhjKCJwaHMtYmx1ZSIpKSwiRXF1aXZvY2FsIiA9IHBoc19jb2xvdXJzKGMoInBocy1tYWdlbnRhIikpLCJQb3NpdGl2ZSIgPSBwaHNfY29sb3VycyhjKCJwaHMtcHVycGxlIikpKQoKCnAgPC0gZ2dwbG90KGRmX2FuYV9iZCwgYWVzKHg9SWdHKSkgKwogICAgIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gInN0YWNrIiwgYmlucz00MCwgZGF0YT1zdWJzZXQoZGZfYW5hX2JkLHRlc3RfcmVzdWx0X3F1YWwgPT0gJ05lZ2F0aXZlJyksIGFlcyhmaWxsPSdOZWdhdGl2ZScpKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAic3RhY2siLCBiaW5zPTQwLCBkYXRhPXN1YnNldChkZl9hbmFfYmQsdGVzdF9yZXN1bHRfcXVhbCA9PSAnUG9zaXRpdmUnKSwgYWVzKGZpbGw9J1Bvc2l0aXZlJykpICsKICAgICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJzdGFjayIsIGJpbnM9NDAsIGRhdGE9c3Vic2V0KGRmX2FuYV9iZCx0ZXN0X3Jlc3VsdF9xdWFsID09ICdFcXVpdm9jYWwnKSwgYWVzKGZpbGw9J0VxdWl2b2NhbCcpKSArCiAgICAgbGFicyh0aXRsZT0nQmxvb2QgRG9ub3JzJyx4PSJBbnRpYm9keSBMZXZlbHMgW0lnR10iLCB5PSJOdW1iZXIgb2YgU2FtcGxlcyIsIGZpbGw9IlJlc3VsdCIpICsKICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpICsKICAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAwLjUpICArCiAgICAgc2NhbGVfeV9sb2cxMCgpIApwCmBgYAoKCgpBbGwgZGF0YQoKYGBge3J9CmJ3IDwtIDAuNQpuIDwtIG5yb3coZGZfYW5hX2JkX3BvcykKcCA8LSBnZ3Bsb3QoZGZfYW5hX2JkX3BvcywgYWVzKHg9SWdHLCBjb2xvcj1uX3Jpc2tfZ3BzKSkgKwogICAgIHN0YXRfYmluKGdlb209InN0ZXAiLGJpbnM9MTAscG9zaXRpb249J2lkZW50aXR5Jywgc2l6ZT0yLCBhbHBoYT0wLjgpICsKICAgICB4bGltKDAsMTApICsKICAgICBsYWJzKHRpdGxlPSdCbG9vZCBEb25vcnMnLHg9IklnRyIsIHk9Ik51bWJlciBvZiBTYW1wbGVzIiwgY29sb3I9Ik51bWJlciBvZiBSaXNrcyAoUUNPVklEKSIpICsKICAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGVfcGhzKHBhbGV0dGUgPSAiYWxsIikgKyAKICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMTAiKSArCiAgICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMC41KSAKcAoKYGBgCgpQb3NpdGl2ZSBzYW1wbGVzIG9ubHkuLi4KCmBgYHtyfQpkZl9hbmFfYmRfcG9zICU+JSBncm91cF9ieShuX3Jpc2tfZ3BzKSAlPiUgc3VtbWFyaXNlKG49bigpKQpgYGAKCmBgYHtyfQpidyA8LSAxMDAKbiA8LSBucm93KGRmX2FuYV9wY19wb3MpCnAgPC0gZ2dwbG90KGRmX2FuYV9wY19wb3MsIGFlcyh4PUlnRywgY29sb3I9bl9yaXNrX2dwcykpICsKICAgICBzdGF0X2JpbihnZW9tPSJzdGVwIixiaW5zPTEwLHBvc2l0aW9uPSdpZGVudGl0eScsIHNpemU9MiwgYWxwaGE9MC44KSArCiAgICAgeGxpbSgwLDIwMDApICsKICAgICBsYWJzKHRpdGxlPSdQcmltYXJ5IENhcmUnLHg9IklnRyIsIHk9Ik51bWJlciBvZiBTYW1wbGVzIiwgY29sb3I9Ik51bWJlciBvZiBSaXNrcyAoUUNPVklEKSIpICsKICAgICBzY2FsZV9jb2xvdXJfZGlzY3JldGVfcGhzKHBhbGV0dGUgPSAiYWxsIikgKyAKICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMTAiKSArCiAgICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMC41KSAKcApgYGAKCgpgYGB7cn0KYncgPC0gMC41Cm4gPC0gbnJvdyhkZl9hbmFfYmRfcG9zKQpwIDwtIGdncGxvdChkZl9hbmFfYmRfcG9zLCBhZXMoeD1JZ0csIGZpbGw9bl9yaXNrX2dwcykpICsKICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IGJ3KSArIHhsaW0oMCwxMStidykgKwogICAgIGxhYnMoeD0iQmxvb2QgRG9ub3JzIElnRyIsIHk9Ik51bWJlciBvZiBTYW1wbGVzIiwgZmlsbD0iTnVtYmVyIG9mIFJpc2tzIChRQ09WSUQpIikgKwogICAgIHNjYWxlX2ZpbGxfZGlzY3JldGVfcGhzKHBhbGV0dGUgPSAibWFpbiIpICsgCiAgICAgc2NhbGVfeV9sb2cxMCgpICsKICAgICB0aGVtZShhc3BlY3QucmF0aW8gPSAwLjUpIApwCmBgYAoKYGBge3J9CmRmX2FuYV9iZApgYGAKCmBgYHtyfQoKZGYgPC0gcmVhZFJEUygiL2NvbmYvRUFWRS9HUGFuYWx5c2lzL2RhdGEvc2Vyb2xvZ3lfcHJpbWNhcmVfbWFyY2gyMi5yZHMiKSAlPiUgZHBseXI6OmxlZnRfam9pbihkZl9xY292aWRfcGMpICU+JSBtdXRhdGUoYWdlX2dyb3VwID0gY3V0KGFnZSxyaWdodD1GQUxTRSwgYnJlYWtzID0gYygwLDEwLDIwLDMwLDQwLDUwLDYwLDcwLDgwLDkwLDEwMDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9YygiMC05IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMC0xOSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjAtMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMzAtMzkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjQwLTQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUwLTU5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYwLTY5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjcwLTc5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjgwLTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjkwKyIpKSkgCmRmCmBgYAoKCmBgYHtyfQptYXhfcHJvcCA8LSAzMCAgICAgIyBjaG9vc2UgdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiB5b3Ugd2FudCB0byBzaG93IApzdGVwIDwtIDUgICAgICAgICAgICMgY2hvb3NlIHRoZSBzcGFjZSB5b3Ugd2FudCBiZXdlZW4gbGFiZWxzIAoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBicmVha3MKYnJlYWtzIDwtIGMoCiAgICBzZXEobWF4X3Byb3AvMTAwICogLTEsIDAgLSBzdGVwLzEwMCwgc3RlcC8xMDApLCAKICAgIDAsIAogICAgc2VxKDAgKyBzdGVwIC8gMTAwLCBtYXhfcHJvcC8xMDAsIHN0ZXAvMTAwKQogICAgKQoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBsaW1pdHMKbGltaXRzIDwtIGMobWF4X3Byb3AvMTAwICogLTEsIG1heF9wcm9wLzEwMCkKCiMjIHRoaXMgcGFydCBkZWZpbmVzIHZlY3RvciB1c2luZyB0aGUgYWJvdmUgbnVtYmVycyB3aXRoIGF4aXMgbGFiZWxzCmxhYmVscyA8LSAgYygKICAgICAgc2VxKG1heF9wcm9wLCBzdGVwLCAtc3RlcCksIAogICAgICAwLCAKICAgICAgc2VxKHN0ZXAsIG1heF9wcm9wLCBzdGVwKQogICAgKQoKCnAxIDwtIGRmICU+JSAKICAjIyBtYWtlIHN1cmUgdGhlIHZhcmlhYmxlcyBhcmUgZmFjdG9ycwogIG11dGF0ZShhZ2VfZ3JvdXAgPSBmYWN0b3Iobl9yaXNrX2dwcyksIAogICAgICAgICBzZXggPSBmYWN0b3Ioc2V4KSkgJT4lCiAgYWdlX3B5cmFtaWQoCiAgICBhZ2VfZ3JvdXAgPSAiYWdlX2dyb3VwIiwKICAgIHNwbGl0X2J5ID0gInNleCIsIAogICAgcHJvcG9ydGlvbiA9IFRSVUUpICsKICAjIyBvbmx5IHNob3cgdGhlIHggYXhpcyBsYWJlbCAob3RoZXJ3aXNlIHJlcGVhdGVkIGluIGFsbCB0aHJlZSBwbG90cykKICBsYWJzKHRpdGxlID0gIlByaW1hcnkgQ2FyZSIsIAogICAgICAgeCA9ICJOIFJpc2sgR3JvdXBzIiwgCiAgICAgICB5ID0gIiUgb2YgU2FtcGxlIikgICsgCiAgIyMgbWFrZSB0aGUgeCBheGlzIHRoZSBzYW1lIGZvciBhbGwgcGxvdHMgCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGJyZWFrcywgCiAgICBsaW1pdHMgPSBsaW1pdHMsIAogICAgbGFiZWxzID0gbGFiZWxzKQoKcDEKYGBgCgpgYGB7cn0KCmRmIDwtIHJlYWRSRFMoIi9jb25mL0VBVkUvR1BhbmFseXNpcy9kYXRhL3Nlcm9sb2d5X3NuYnRzX21hcmNoMjIucmRzIikgJT4lIGRwbHlyOjpsZWZ0X2pvaW4oZGZfcWNvdmlkX2JkKQpkZgpgYGAKYGBge3J9Cm1heF9wcm9wIDwtIDQwICAgICAjIGNob29zZSB0aGUgaGlnaGVzdCBwcm9wb3J0aW9uIHlvdSB3YW50IHRvIHNob3cgCnN0ZXAgPC0gNSAgICAgICAgICAgIyBjaG9vc2UgdGhlIHNwYWNlIHlvdSB3YW50IGJld2VlbiBsYWJlbHMgCgojIyB0aGlzIHBhcnQgZGVmaW5lcyB2ZWN0b3IgdXNpbmcgdGhlIGFib3ZlIG51bWJlcnMgd2l0aCBheGlzIGJyZWFrcwpicmVha3MgPC0gYygKICAgIHNlcShtYXhfcHJvcC8xMDAgKiAtMSwgMCAtIHN0ZXAvMTAwLCBzdGVwLzEwMCksIAogICAgMCwgCiAgICBzZXEoMCArIHN0ZXAgLyAxMDAsIG1heF9wcm9wLzEwMCwgc3RlcC8xMDApCiAgICApCgojIyB0aGlzIHBhcnQgZGVmaW5lcyB2ZWN0b3IgdXNpbmcgdGhlIGFib3ZlIG51bWJlcnMgd2l0aCBheGlzIGxpbWl0cwpsaW1pdHMgPC0gYyhtYXhfcHJvcC8xMDAgKiAtMSwgbWF4X3Byb3AvMTAwKQoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBsYWJlbHMKbGFiZWxzIDwtICBjKAogICAgICBzZXEobWF4X3Byb3AsIHN0ZXAsIC1zdGVwKSwgCiAgICAgIDAsIAogICAgICBzZXEoc3RlcCwgbWF4X3Byb3AsIHN0ZXApCiAgICApCgoKcDIgPC0gZGYgJT4lIAogICMjIG1ha2Ugc3VyZSB0aGUgdmFyaWFibGVzIGFyZSBmYWN0b3JzCiAgbXV0YXRlKGFnZV9ncm91cCA9IGZhY3RvcihuX3Jpc2tfZ3BzKSwgCiAgICAgICAgIHNleCA9IGZhY3RvcihzZXgpKSAlPiUKICBhZ2VfcHlyYW1pZCgKICAgIGFnZV9ncm91cCA9ICJhZ2VfZ3JvdXAiLAogICAgc3BsaXRfYnkgPSAic2V4IiwgCiAgICBwcm9wb3J0aW9uID0gVFJVRSkgKwogICMjIG9ubHkgc2hvdyB0aGUgeCBheGlzIGxhYmVsIChvdGhlcndpc2UgcmVwZWF0ZWQgaW4gYWxsIHRocmVlIHBsb3RzKQogIGxhYnModGl0bGUgPSAiQmxvb2QgRG9ub3JzIiwgCiAgICAgICB4ID0gIk4gUmlzayBHcm91cHMiLCAKICAgICAgIHkgPSAiJSBvZiBTYW1wbGUiKSAgKyAKICAjIyBtYWtlIHRoZSB4IGF4aXMgdGhlIHNhbWUgZm9yIGFsbCBwbG90cyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzLCAKICAgIGxpbWl0cyA9IGxpbWl0cywgCiAgICBsYWJlbHMgPSBsYWJlbHMpCgpwMgpgYGAKCgpgYGB7cn0KZGYgPC0gcmVhZFJEUygiL2NvbmYvRUFWRS9HUGFuYWx5c2lzL2RhdGEvRUFWRV9kZW1vZ3JhcGhpY3NfU0sucmRzIikgJT4lIGxlZnRfam9pbihkZl9xY292aWQpCmRmCmBgYAoKYGBge3J9CgptYXhfcHJvcCA8LSAzMCAgICAgIyBjaG9vc2UgdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiB5b3Ugd2FudCB0byBzaG93IApzdGVwIDwtIDUgICAgICAgICAgICMgY2hvb3NlIHRoZSBzcGFjZSB5b3Ugd2FudCBiZXdlZW4gbGFiZWxzIAoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBicmVha3MKYnJlYWtzIDwtIGMoCiAgICBzZXEobWF4X3Byb3AvMTAwICogLTEsIDAgLSBzdGVwLzEwMCwgc3RlcC8xMDApLCAKICAgIDAsIAogICAgc2VxKDAgKyBzdGVwIC8gMTAwLCBtYXhfcHJvcC8xMDAsIHN0ZXAvMTAwKQogICAgKQoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBsaW1pdHMKbGltaXRzIDwtIGMobWF4X3Byb3AvMTAwICogLTEsIG1heF9wcm9wLzEwMCkKCiMjIHRoaXMgcGFydCBkZWZpbmVzIHZlY3RvciB1c2luZyB0aGUgYWJvdmUgbnVtYmVycyB3aXRoIGF4aXMgbGFiZWxzCmxhYmVscyA8LSAgYygKICAgICAgc2VxKG1heF9wcm9wLCBzdGVwLCAtc3RlcCksIAogICAgICAwLCAKICAgICAgc2VxKHN0ZXAsIG1heF9wcm9wLCBzdGVwKQogICAgKQoKCnAzIDwtIGRmICU+JSAKICAjIyBtYWtlIHN1cmUgdGhlIHZhcmlhYmxlcyBhcmUgZmFjdG9ycwogIG11dGF0ZShhZ2VfZ3JvdXAgPSBmYWN0b3Iobl9yaXNrX2dwcyksIAogICAgICAgICBzZXggPSBmYWN0b3IoU2V4KSkgJT4lCiAgYWdlX3B5cmFtaWQoCiAgICBhZ2VfZ3JvdXAgPSAiYWdlX2dyb3VwIiwKICAgIHNwbGl0X2J5ID0gInNleCIsIAogICAgcHJvcG9ydGlvbiA9IFRSVUUpICsKICAjIyBvbmx5IHNob3cgdGhlIHggYXhpcyBsYWJlbCAob3RoZXJ3aXNlIHJlcGVhdGVkIGluIGFsbCB0aHJlZSBwbG90cykKICBsYWJzKHRpdGxlID0gIkVBVkUtSUkiLCAKICAgICAgIHggPSAiTiBSaXNrIEdyb3VwcyIsIAogICAgICAgeSA9ICIlIG9mIFNhbXBsZSIpICArIAogICMjIG1ha2UgdGhlIHggYXhpcyB0aGUgc2FtZSBmb3IgYWxsIHBsb3RzIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBicmVha3MsIAogICAgbGltaXRzID0gbGltaXRzLCAKICAgIGxhYmVscyA9IGxhYmVscykKCnAzCmBgYAoKCgoKYGBge3IsZmlnLmhlaWdodD0yLjUsZmlnLndpZHRoPTEyfQpncmlkLmFycmFuZ2UocDMscDEscDIsbmNvbD0zLG5yb3c9MSkKYGBgCgoKCgoKCgpgYGB7cixmaWcuaGVpZ2h0PTIuNSxmaWcud2lkdGg9MTJ9CgptYXhfcHJvcCA8LSA0MCAgICAgIyBjaG9vc2UgdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiB5b3Ugd2FudCB0byBzaG93IApzdGVwIDwtIDUgICAgICAgICAgICMgY2hvb3NlIHRoZSBzcGFjZSB5b3Ugd2FudCBiZXdlZW4gbGFiZWxzIAoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBicmVha3MKYnJlYWtzIDwtIGMoCiAgICBzZXEobWF4X3Byb3AvMTAwICogLTEsIDAgLSBzdGVwLzEwMCwgc3RlcC8xMDApLCAKICAgIDAsIAogICAgc2VxKDAgKyBzdGVwIC8gMTAwLCBtYXhfcHJvcC8xMDAsIHN0ZXAvMTAwKQogICAgKQoKIyMgdGhpcyBwYXJ0IGRlZmluZXMgdmVjdG9yIHVzaW5nIHRoZSBhYm92ZSBudW1iZXJzIHdpdGggYXhpcyBsaW1pdHMKbGltaXRzIDwtIGMobWF4X3Byb3AvMTAwICogLTEsIG1heF9wcm9wLzEwMCkKCiMjIHRoaXMgcGFydCBkZWZpbmVzIHZlY3RvciB1c2luZyB0aGUgYWJvdmUgbnVtYmVycyB3aXRoIGF4aXMgbGFiZWxzCmxhYmVscyA8LSAgYygKICAgICAgc2VxKG1heF9wcm9wLCBzdGVwLCAtc3RlcCksIAogICAgICAwLCAKICAgICAgc2VxKHN0ZXAsIG1heF9wcm9wLCBzdGVwKQogICAgKQoKCiAKCnAyIDwtIGRmICU+JSAKICAjIyBtYWtlIHN1cmUgdGhlIHZhcmlhYmxlcyBhcmUgZmFjdG9ycwogIG11dGF0ZShhZ2VfZ3JvdXAgPSBmYWN0b3IoYWdlX2dyb3VwKSwgCiAgICAgICAgIHNleCA9IGZhY3RvcihTZXgpKSAlPiUKICBhZ2VfcHlyYW1pZCgKICAgIGFnZV9ncm91cCA9ICJhZ2VfZ3JvdXAiLAogICAgc3BsaXRfYnkgPSAic2V4IiwgCiAgICBwcm9wb3J0aW9uID0gVFJVRSkgKwogICMjIG9ubHkgc2hvdyB0aGUgeCBheGlzIGxhYmVsIChvdGhlcndpc2UgcmVwZWF0ZWQgaW4gYWxsIHRocmVlIHBsb3RzKQogIGxhYnModGl0bGUgPSAiQmxvb2QgRG9ub3JzIiwgCiAgICAgICB4ID0gIk4gUmlzayBHcm91cHMiLCAKICAgICAgIHkgPSAiJSBvZiBTYW1wbGUiKSAgKyAKICAjIyBtYWtlIHRoZSB4IGF4aXMgdGhlIHNhbWUgZm9yIGFsbCBwbG90cyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzLCAKICAgIGxpbWl0cyA9IGxpbWl0cywgCiAgICBsYWJlbHMgPSBsYWJlbHMpCgpncmlkLmFycmFuZ2UocDEscDIscDIsbmNvbD0zLG5yb3c9MSkKYGBgCg==